package com.zlm.design.flyweight;

import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * {@code }
 *
 * @author zlm
 * @since 2025/6/24
 */
public class BufferPool {

    // 总大小
    private final long totalSize;
    // 一个复用内存大小
    private final int slotSize;
    // 剩余大小
    private long free;
    // 复用内存的队列
    private final Deque<ByteBuffer> slotQueue = new ArrayDeque<>();
    // 等待被人释放内存后申请内存的队列
    private final Deque<Condition> waitQueue = new ArrayDeque<>();

    private final Lock lock = new ReentrantLock();

    public BufferPool(long totalSize, int slotSize){
        this.totalSize = totalSize;
        this.slotSize = slotSize;
        this.free = totalSize;
    }

    /**
     * 申请内存
     * @param size
     * @return
     */
    public ByteBuffer allocate(int size,long timeout) throws InterruptedException {
        if(size > totalSize || size <= 0){
            throw new IllegalArgumentException("你的申请容量异常size:"+size);
        }
        lock.lock();
        try{

            if(size == slotSize && !slotQueue.isEmpty()){

                //如果申请的size和可复用内存大小一致，并且可复用队列的存在，则直接从队列中获取
                return slotQueue.pollFirst();
            }
            if((free + slotQueue.size() * slotSize) >= size){
                //如果剩余内存大小+可复用队列长度乘以单个复用内存大小，大于等于申请的size，则说明可以申请
                freeUp(size);
                free -= size;
                return ByteBuffer.allocate(size);
            }
            //如果申请的内存大小大于剩余内存大小+可复用队列长度乘以单个复用内存大小，则说明需要等待
            Condition condition = lock.newCondition();
            waitQueue.addLast(condition);
            long remainTime = timeout;
            try {
                while (true){
                    long start = System.currentTimeMillis();
                    // true 表示被唤醒 false 表示被超时中断
                    boolean wakeUp = condition.await(remainTime, TimeUnit.MILLISECONDS);
                    if(!wakeUp){
                        //超时
                        throw new RuntimeException("规定时间内不能申请到需要的内存");
                    }
                    if(size == slotSize && !slotQueue.isEmpty()){

                        //如果申请的size和可复用内存大小一致，并且可复用队列的存在，则直接从队列中获取
                        return slotQueue.pollFirst();
                    }
                    if((free + slotQueue.size() * slotSize) >= size){
                        //如果剩余内存大小+可复用队列长度乘以单个复用内存大小，大于等于申请的size，则说明可以申请
                        freeUp(size);
                        free -= size;
                        return ByteBuffer.allocate(size);
                    }
                    remainTime -= (System.currentTimeMillis() - start);
                }
            } finally {
                //移除等待队列元素
                waitQueue.remove(condition);
            }
        }finally {
            //如果存在等待队列，并且剩余内存和可复用队列都不为空，则唤醒一个等待的线程
            if(!waitQueue.isEmpty() && !(free == 0 && slotQueue.isEmpty())){
                //唤醒一个等待的线程
                waitQueue.peekFirst().signal();
            }
            lock.unlock();
        }
    }

    /**
     * 释放内存
     */
    private void freeUp(int size){
        while (free < size && !slotQueue.isEmpty()){
            free += slotQueue.pollFirst().capacity();
        }
    }

    /**
     * 还回内存
     * @param buffer
     */
    public void deallocate(ByteBuffer buffer){
        lock.lock();
        try{
            if(buffer.capacity() == slotSize){
                //清空缓存
                buffer.clear();
                //如果释放的内存大小和可复用内存大小一致，则加入可复用队列
                slotQueue.addLast(buffer);
            }else{
                free += buffer.capacity();
            }
            if(!waitQueue.isEmpty()){
                //如果存在等待队列，则唤醒一个等待的线程
                waitQueue.peekFirst().signal();
            }
        }finally {
            lock.unlock();
        }
    }
}
